package com.hsj.completablefuture;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 *
 * @ClassName:CompletableFutureDemo
 * @Description:多线程并发任务,取结果归集
 * @author diandian.zhang
 * @date 2017年6月14日下午12:44:01
 */
public class CompletableFutureDemo {
    public static void main(String[] args) {
        Long start = System.currentTimeMillis();
        //结果集
        List<String> list = new ArrayList<String>();
        //定长10线程池
        ExecutorService exs = Executors.newFixedThreadPool(10);
        List<CompletableFuture<String>> futureList = new ArrayList<>();
        //任务
        final List<Integer> taskList = new ArrayList(){{
            add(2); add(1); add(3); add(6); add(4); add(5); add(8); add(9); add(7); add(10);
        }};
        try {

//            方式一：全流式处理转换成CompletableFuture[]+组装成一个无返回值CompletableFuture，join等待执行完毕。返回结果whenComplete获取
            CompletableFuture[] cfHaveBack = taskList
                    .stream()
                    .map(
                            object-> CompletableFuture.supplyAsync(()->calc(object), exs)
                                    .thenApply(integer->Integer.toString(integer))//中间的转换
                                    //如需获取任务完成先后顺序，此处代码即可
                                    .whenComplete((v, e) -> {
                                        System.out.println("任务"+v+"完成!result="+v+"，异常 e="+e+","+new Date());
                                        list.add(v);
                                    })
                    ).toArray(CompletableFuture[]::new);
            //等待总任务完成，但是封装后无返回值，必须自己whenComplete()获取
            CompletableFuture.allOf(cfHaveBack).join();
            System.out.println("任务完成，结果list="+list+"；耗时="+(System.currentTimeMillis()-start));

            //华丽丽的分割线
            System.out.println("///////////////////////////////////////////////////////////////////");

            //方式二：全流式处理转换成CompletableFuture[]+组装成一个无返回值CompletableFuture，join等待执行完毕。无返回值。
            CompletableFuture[] cfNoBack = taskList
                    .stream()
                    .map(
                            object-> CompletableFuture.runAsync(
                                    ()->calcV2(taskList,object),
                                    exs)
                    ).toArray(CompletableFuture[]::new);
            //等待总任务完成，但是封装后无返回值，必须自己whenComplete()获取
            CompletableFuture.allOf(cfNoBack).join();
            System.out.println("任务完成，结果taskList="+taskList+"；耗时="+(System.currentTimeMillis()-start));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            exs.shutdown();
        }
    }

    /**
     * @Description:有返回值
     * @author HeShengjin 2356899074@qq.com
     * @date 2021/11/18 10:21
     */
    public static Integer calc(Integer i){
        try {
            if(i==1){
                //任务1耗时3秒
                Thread.sleep(3000);
            }else if(i==5){
                //任务5耗时5秒
                Thread.sleep(5000);
            }else{
                //其它任务耗时1秒
                Thread.sleep(1000);
            }
            System.out.println("task线程："+Thread.currentThread().getName()+"任务i="+i+",完成！+"+new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return i;
    }
    /**
     * @Description:没有返回值
     * @author HeShengjin 2356899074@qq.com
     * @date 2021/11/18 10:21
     */
    public static void calcV2(List<Integer> taskList,Integer i){
        try {
            if(i==1){
                //任务1耗时3秒
                Thread.sleep(3000);
            }else if(i==5){
                //任务5耗时5秒
                Thread.sleep(5000);
            }else{
                //其它任务耗时1秒
                Thread.sleep(1000);
            }
            //自己的业务逻辑
            System.out.println("task线程："+Thread.currentThread().getName()+"任务i="+i+",完成！+"+new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}